package com.qdev.apps.core.service;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.sf.hibernate.Criteria;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.ScrollableResults;
import net.sf.hibernate.Session;
import net.sf.hibernate.expression.Criterion;
import net.sf.hibernate.expression.Order;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;

import com.qdev.apps.core.domain.Entity;
import com.qdev.apps.core.persistence.PaginationSupport;
import com.qdev.apps.core.persistence.impl.CriteriaResultsCounter;

/**
 * @author Quake Wang
 * @since 2004-6-4
 * @version $Revision: 1.8 $
 *  
 */
public class AbstractService extends HibernateDaoSupport {
    private static final Log log = LogFactory.getLog(AbstractService.class);

    private boolean cacheQueries = false;
    private String queryCacheRegion;

    protected void initDao() throws Exception {
        super.initDao();
        getHibernateTemplate().setCacheQueries(cacheQueries);
        if (queryCacheRegion != null) {
            getHibernateTemplate().setQueryCacheRegion(queryCacheRegion);
        }
        getHibernateTemplate().setExposeNativeSession(true);
    }

    public void setCacheQueries(boolean cacheQueries) {
        this.cacheQueries = cacheQueries;
    }

    public void setQueryCacheRegion(String queryCacheRegion) {
        this.queryCacheRegion = queryCacheRegion;
    }

    //helper methods for create/update/delete
    public void create(Entity entity) {
        getHibernateTemplate().save(entity);
    }

    public void update(Entity entity) {
        getHibernateTemplate().update(entity);
    }

    public void delete(Entity entity) {
        getHibernateTemplate().delete(entity);
    }

    //helper methods for query
    public Object loadById(final Class entity, final Serializable id) {
        return getHibernateTemplate().get(entity, id);
    }

    public List findAll(final Class entity) {
        return getHibernateTemplate().find("from " + entity.getName());
    }

    public List findByNamedQuery(final String namedQuery) {
        return getHibernateTemplate().findByNamedQuery(namedQuery);
    }

    public List findByNamedQuery(final String query, final Object parameter) {
        return getHibernateTemplate().findByNamedQuery(query, parameter);
    }

    public List findByNamedQuery(final String query, final Object[] parameters) {
        return getHibernateTemplate().findByNamedQuery(query, parameters);
    }

    public List find(final String query) {
        return getHibernateTemplate().find(query);
    }
    
    public PaginationSupport find(final Class entity, final Criterion c, final PaginationSupport ps) {
        return find(entity, c, ps, null);
    }

    public PaginationSupport find(final Class entity, final Criterion c, final PaginationSupport ps, final Order[] orders) {
        return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Criteria criteria = session.createCriteria(entity).add(c);
                ps.setTotalCount(CriteriaResultsCounter.count(session, criteria));
                if (orders != null) {
                    for (int i = 0; i < orders.length; i++) {
                        criteria.addOrder(orders[i]);
                    }
                }
                criteria.setFirstResult(ps.getStartIndex()).setMaxResults(ps.getCountOnEachPage());

                ps.setItems(criteria.list());

                return ps;
            }
        });
    }

    public PaginationSupport find(final String sql, final Object[] parameters, final PaginationSupport ps) {
        return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(sql);
                for (int i = 0; i < parameters.length; i++) {
                    Object parameter = parameters[i];
                    query.setParameter(i, parameter);
                }
                ScrollableResults sr = query.scroll();
                sr.last();
                ps.setTotalCount(sr.getRowNumber() + 1);
                query.setFirstResult(ps.getStartIndex()).setMaxResults(ps.getCountOnEachPage());
                ps.setItems(query.list());
                return ps;
            }
        });
    }

    public List findByNamedQueryHavingMassIn(String queryName, String[] names, Object[] values, String inName) {
        Set s = new HashSet();
        int maxSize = 1000;
        if(ArrayUtils.indexOf(names,inName) < 0) {
            log.error("No such name in parameter list - " + inName);
            return new ArrayList();
        } else {
            List in = (List) values[ArrayUtils.indexOf(names,inName)]; 
	        //get smallest int which is greater than
	        int queryTimes = in.size()/maxSize;
	        if(queryTimes*maxSize < in.size()) queryTimes++;
	        for(int i=0; i<queryTimes; i++) {
	            List t = in.subList(i*maxSize, Math.min((i+1)*maxSize,in.size()));
	            List tempValues = new ArrayList();
	            for(int j=0; j<names.length; j++) {
	                if(j == ArrayUtils.indexOf(names,inName)) {
	                    tempValues.add(t);
	                } else {
	                    tempValues.add(values[j]);
	                }
	            }
	            s.addAll(getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, names, (Object[]) tempValues.toArray(new Object[tempValues.size()])));
	        }
	        return new ArrayList(s);
        }
    }
    
}